<?php
/*
 * 智能加载器 Loader
 *
 * 此类应该只被Controller中心控制类所实例化，用于智能化加载页面和某些文件
 */

class Loader {
	public function __construct() {
		//引用自身
		$this->load = &$this;
	}

	/**
	 * 视图装载器
	 *
	 * 此函数将页面及所需数据都聚合一起输出
	 *
	 * @param string $viewName 视图名称 , array $params 传到视图的数据 , string $viewPath 视图路径
	 * @return string 视图文件内容
	 */

	public function view($viewName, $params = array(), $viewPath = VIEWPATH) {
		//设置防盗链session（true代表允许访问资源，false代表锁定资源）
		setSession('resAllow', true);
		$viewPath = $viewPath . $viewName . '.php';
		if(!file_exists($viewPath)) {
			_log('找不到视图文件' . $viewName);
			showError('error', '视图无法加载', '未找到对应的视图文件，这将导致无法显示页面');
		}
		echo "<!-- VCMINI FRAME -->\n";
		ob_start();
		//引入视图文件
		include $viewPath;
		$buffer = ob_get_contents();
		ob_end_clean();
		$buffer = $this->setParams($buffer, $params);
		echo $buffer;
	}

	public function model($modelName, $modelPath = MODELPATH, $params = array()) {
		$model = &loadClass($modelName, $params, $modelPath);
		return $model;
	}

	/**
	 * 模板装载器
	 *
	 * 此函数将模板页面结合数据输出
	 *
	 * @param string $templateName 视图名称 , array $params 传到模板的数据 , string $templatePath 模板路径
	 * @return void 输出模板文件内容
	 */

	public function template($templateName, $params = array(), $templatePath = TEMPPATH) {
		$templatePath = $templatePath . $templateName . '.php';
		if(!file_exists($templatePath)) {
			_log('找不到模板文件' . $templateName);
			showError('error', '模板无法加载', '未找到对应的视图模板文件，这将导致无法显示页面模板', 404);
		}
		ob_start();
		include $templatePath;
		$template = ob_get_contents();
		ob_end_clean();
		//填入参数
		foreach($params as $key => $val) {
			$template = str_replace('[' . $key . ']', $val, $template);
		}
		//如果请求的模板为Meta部分则从配置文件中读取关键字与描述填入再输出
		if($templateName == 'Meta') {
			//取得关键字
			$keyWords = getConfigItem('keyWords');
			//取得描述
			$descriptions = getConfigItem('descriptions');
			$template = str_replace('[keyWords]', "'" . $keyWords . "'", $template);
			$template = str_replace('[descriptions]', "'" . $descriptions . "'", $template);
			//输出模板内容
			return $template;
		}
		else {
			//直接输出模板内容
			$template = $this->setParams($template);
			return $template;
		}
	}

	/**
	 * 标题连接函数
	 *
	 * 将标题与配置的标题后缀连接
	 *
	 * @param string $title 页面标题
	 * @return string 连接后的标题内容
	 */

	public function title($title = 'unknown') {
		$barSuffix = getConfigItem('barSuffix');
		if(empty($barSuffix)) {
			$title = '<title>' . $title . '</title>';
		}
		else {
			$title =  '<title>' . $title . ' - ' . $barSuffix . '</title>';
		}
		return $title;
	}

	/**
	 * 调试消息输出函数
	 *
	 * 获取到之前通过showMsg添加的消息并输出
	 *
	 * @param void
	 * @return 输出消息到浏览器控制台
	 */

	public function msg() {
		$msg = getSession('msgTemp');
		delSession('msgTemp');
		if(!empty($msg)) {
			return '<script>console.log(' . json_encode($msg) . ');</script>';
		}
	}

	/**
	 * 库装载函数
	 *
	 * 导入库文件并嵌入框架
	 *
	 * @param string $libName 库名 , array $params 传入库的参数 , string $libPath 库文件所在路径
	 * @return class 库实例
	 */

	public function lib($libName, $params = array(), $libPath = null) {
		if(is_null($libPath)) {
			$libPath = SYSPATH . 'libs/';
		}
		$lib = &loadClass($libName, $params, $libPath);
		return $lib;
	}

	/**
	//此方法临时弃用
	public function plugin($pluginName, $params = array(), $pluginPath = null) {
		if(is_null($pluginPath)) {
			$pluginPath = SYSPATH . 'plugins/';
		}
		$plugin = &loadClass($pluginName, $params, $pluginPath);
		return $plugin;
	}
	**/

	/**
	 * 包装载函数
	 *
	 * 此函数能够导入包，能够加载依赖后再导入包
	 *
	 * @param string $packageName 包名 , array $relyonList 包依赖名列表 , array $params 传递给包的参数 , string $packagePath 包所在路径
	 * @return class 包
	 */

	public function package($packageName, $relyonList = array(), $params = array(), $packagePath = null) {
		if(is_null($packagePath)) {
			$packagePath = SYSPATH . 'packages/';
		}
		$package = &loadPackage($packageName, $relyonList, $params, $packagePath);
		return $package;
	}

	/**
	 * 数据库装载函数
	 *
	 * 此函数能够用于多数据库操作
	 *
	 * @param string $dbConfName 数据库配置项目名
	 */

	public function database($dbConfName = 'default') {
		$dbConfs = getConfigItem('database');
		if(!isset($dbConfs[$dbConfName])) {
			showError('error', '数据库装载出错', $dbConfName . '的数据库配置信息不存在');
		}
		$db = &loadClass('DB');
		$db->setConfig($dbConfs[$dbConfName]);
		return clone $db;
	}

	/**
	 * js装载函数
	 *
	 * 此函数能够装载js文件
	 *
	 * @param string js文件名1 , js文件名2 , ...
	 * @return js文件内容
	 */

	public function js($jsNameList) {
		$output = '';
		foreach($jsNameList as $jsName) {
			$jsName = trim($jsName);
			//如果传入的不是文件名是链接url，直接设置js路径为该url
			if(preg_match('@(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))@', $jsName)) {
				$jsPath = $jsName;
			}
			else {
				//构造获取js的链接
				$jsPath = BASEDIR . 'Files/JavaScript/' . $jsName;
			}
			$output .= "<script type='text/javascript' src='" . $jsPath . "'></script>\r\n";
		}
		return $output;
	}

	/**
	 * css装载函数
	 *
	 * 此函数能够装载css文件
	 *
	 * @param string css文件名1 , css文件名2 , ...
	 * @return css文件内容
	 */

	public function css($cssNameList) {
		$output = '';
		foreach($cssNameList as $cssName) {
			$cssName = trim($cssName);
			//如果传入的不是文件名是链接url，直接设置css路径为该url
			if(preg_match('@(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))@', $cssName)) {
				$cssPath = $cssName;
			}
			else {
				//构造获取css的链接
				$cssPath = BASEDIR . 'Files/StyleScript/' . $cssName;
			}
			$output .= "<link type='text/css' href='" . $cssPath . "' rel='stylesheet'>\r\n";
		}
		return $output;
	}

	/**
	 * 加载图标函数
	 *
	 * 此函数可加载到对应的图标文件
	 *
	 * @param string $iconName 图标名称
	 * @return void 输除设置icon的html标签
	 */

	public function favicon($iconName = 'favicon') {
		
		if(preg_match('@(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))@', $iconName)) {
				$iconPath = $iconName;
		}
		else {
			$iconPath = BASEDIR . 'Files/Ico/' . $iconName;
		}
		return "<link type='image/x-icon' href='{$iconPath}' rel='shortcut icon'>";
	}

	/**
	 * js数据暂存函数
	 *
	 * 此函数将传入的数据数组转换存储在session中
	 *
	 * @param array $params 暂存的数据数组
	 * @return void 将数据转换暂存在jsData session 中
	 */

	private function getJsData($jsParams = array()) {
		$jsParams = json_encode($jsParams);
		return "<script type='text/javascript'>var jsParams = {$jsParams}</script>\r\n";
	}

	/**
	 * 视图输出前的数据附加函数
	 *
	 * 此函数将检查前端页面中存在的数据标签然后将标签替换为数据
	 *
	 * @param string $content 前端页面内容
	 * @return string 返回附加完数据的前端页面内容
	 */

	private function setParams($content, $jsData = array()) {
		//替换base标签为根目录
		$content = str_replace('[base]', BASEDIR, $content);
		$result = array();
		//匹配需要替换的项目
		preg_match_all('/(?:\[)(.*)(?:\])/i', $content, $result);
		if(is_array($result) && is_array($result[1]) && isset($result[1][0])) {
			$result = $result[1];
			foreach($result as $val) {
				//组合需要替换的标签字符串
				$replace = '[' . $val . ']';
				$val = trim($val);
				//如果是msg标签不用于批量处理，仅调用msg函数预备输出
				if($val == 'msg') {
					$_result = $this->msg();
					$content = str_replace($replace, $_result, $content);
					continue;
				}
				else if($val == 'JsData') {
					$_result = $this->getJsData($jsData);
					$content = str_replace($replace, $_result, $content);
					continue;
				}
				//分割属性
				$item = explode('|', $val);
				//js和css的标签需要单独做参数分割处理
				if($item[0] == 'js' || $item[0] == 'css') {
					$item[1] = str_replace('，', ',', $item[1]);
					$item[1] = explode(',', trim($item[1]));
				}
				//分别调用他们的处理方法
				$_result = call_user_func(array($this, trim($item[0])), $item[1]);
				//替换标签为数据
				$content = str_replace($replace, $_result, $content);
			}
		}
		return $content;
	}
}